﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Web.SessionState;
using Microsoft.SqlServer.Types;

namespace MapViewer
{
    //---------------------------------------------------------------------
    //---------------------------------------------------------------------
    // Extension Methods. Extend the functionality of the .net classes
    // for our own purposes.
    //---------------------------------------------------------------------
    //---------------------------------------------------------------------

    //---------------------------------------------------------------------
    // extension method for TextBox - "safe" Get and Set functions 
    // for the TextBox Text
    //---------------------------------------------------------------------
    internal static class TextBox_extensions
    {
        internal static float Get_Float(this TextBox box)
        {
            if (String.IsNullOrEmpty(box.Text))
                box.Text = "0";
            return (float)Convert.ToDouble(box.Text);

        }

        internal static void Set_Float(this TextBox box, float value)
        {
            box.Text = value.ToString();
        }
    }
    
    //---------------------------------------------------------------------
    // Extension methods for "safe" getting and setting values in the Session
    //---------------------------------------------------------------------
    internal static class Session_extensions
    {
        internal static float Get_Float(this HttpSessionState Session, String key)
        {
            if (null == Session[key])
                Session[key] = 0;
            return (float)Convert.ToDouble(Session[key]);

        }

        internal static void Set_Float(this HttpSessionState Session, String key, float value)
        {
            Session[key] = value;
        }

        internal static String Get_String(this HttpSessionState Session, String key)
        {
            if (null == Session[key])
                Session[key] = "";
            return Session[key].ToString();
        }

        internal static void Set_String(this HttpSessionState Session, String key, String value)
        {
            Session[key] = value;
        }

        internal static Boolean Get_Boolean(this HttpSessionState Session, String key)
        {
            if (null == Session[key])
                Session[key] = 0;
            return (Boolean)Convert.ToBoolean(Session[key]);

        }

        internal static void Set_Boolean(this HttpSessionState Session, String key, Boolean value)
        {
            Session[key] = value;
        }
    }

    //---------------------------------------------------------------------
    // extension method for StateBag - "safe" Get and Set functions 
    // for the ViewState
    //---------------------------------------------------------------------
    internal static class ViewState_extensions
    {
        internal static float Get_Float(this StateBag bag, String key)
        {
            if (null == bag[key])
                bag[key] = 0;
            return (float)Convert.ToDouble(bag[key]);

        }

        internal static void Set_Float(this StateBag bag, String key, float value)
        {
            bag[key] = value;
        }

        internal static String Get_String(this StateBag bag, String key)
        {
            if (null == bag[key])
                bag[key] = "";
            return bag[key].ToString();

        }

        internal static void Set_String(this StateBag bag, String key, String value)
        {
            bag[key] = value;
        }
    }

    //---------------------------------------------------------------------
    // extension method for SqlGeometry - convert points to GraphicsPath
    //---------------------------------------------------------------------
    internal static class Geometry_extensions
    {
        //---------------------------------------------------------------------    
        // Convert SqlGeometry to graphicpaths
        //---------------------------------------------------------------------    
        internal static List<GraphicsPath> ConvertToPaths(this SqlGeometry Geom, PointF offsets, float scale)
        {
            List<GraphicsPath> list = new List<GraphicsPath>((int)Geom.STNumGeometries());
            GraphicsPath path = new GraphicsPath();

            for (int index = 0; index < Geom.STNumGeometries(); index++)
            {
                // access geometry and extract its points
                SqlGeometry subGeom = Geom.STGeometryN(index + 1);

                if ("Polygon" == subGeom.STGeometryType())
                {
                    // process exterior ring
                    PointF[] Points = subGeom.STExteriorRing().PointsFromGeom();
                    path.AddPolygon(Points);

                    // process any interior rings
                    for (int interiorindex = 0; interiorindex < subGeom.STNumInteriorRing(); interiorindex++)
                    {
                        Points = subGeom.STInteriorRingN(interiorindex+1).PointsFromGeom();
                        path.AddPolygon(Points);
                    }
                }
                else
                {
                    PointF[] Points = subGeom.PointsFromGeom();
                    path.AddLines(Points);
                }

            }

            // Move and scale this path. This will a) position the drawn shapes 
            // where we can see them and b)scale them to fit in the requested 
            // bitmap size.
            Matrix m = new Matrix();
            m.Translate(-offsets.X, -offsets.Y);
            m.Scale(scale, scale, MatrixOrder.Append);
            path.Transform(m);

            // add path to the list
            list.Add(path);

            return list;
        }

        //---------------------------------------------------------------------
        // Extract all the points from this geometry
        //---------------------------------------------------------------------
        private static PointF[] PointsFromGeom(this SqlGeometry subGeom)
        {
            PointF[] Points;
            Points = new PointF[(Int32)(subGeom.STNumPoints())];

            for (int PointFIndex = 0; PointFIndex < subGeom.STNumPoints(); PointFIndex++)
            {
                SqlGeometry geomPointF = subGeom.STPointN(PointFIndex + 1);
                Points[PointFIndex] = new PointF((float)geomPointF.STX.Value, (float)geomPointF.STY.Value);
            }
            return Points;
        }

        //---------------------------------------------------------------------
        // Determine the location to print the text value in or near a shape
        //---------------------------------------------------------------------
        internal static PointF[] GetPrintLocation(this SqlGeometry geometry)
        {
            float length = 0;
            PointF[] points;

            // If we have lines, find the middle
            if ("MultiLineString" == geometry.STGeometryType() ||
                "LineString" == geometry.STGeometryType())
            {
                //hold two points for a line segment
                points = new PointF[2];

                // calculate half the length of the line (midpoint)
                length = (float)geometry.STLength();
                float half = 0.5F * length;

                // try to find the segment that contains the midpoint
                float current = 0;
                for (int geoindex = 1; geoindex <= geometry.STNumGeometries(); geoindex++)
                {
                    SqlGeometry temp = geometry.STGeometryN(geoindex);
                    for (int index = 1; index < temp.STNumPoints(); index++)
                    {
                        SqlGeometry p1 = temp.STPointN(index);
                        SqlGeometry p2 = temp.STPointN(index + 1);
                        current += (float)p1.STDistance(p2);
                        if (current > half)
                        {
                            // this segment contains the middle
                            points[0] = new PointF((float)p1.STX, (float)p1.STY);
                            points[1] = new PointF((float)p2.STX, (float)p2.STY);
                            index = (int)temp.STNumPoints();
                            geoindex = (int)geometry.STNumGeometries();
                        }
                    }
                }
            }
            else // we have polygons so find the center of the largest subgeometry
            {
                points = new PointF[1];
                float area = 0;

                // find the largest subgeometry and save its midpoint
                for (int geoindex = 1; geoindex <= geometry.STNumGeometries(); geoindex++)
                {
                    SqlGeometry p1 = geometry.STGeometryN(geoindex);

                    if (area < p1.STArea())
                    {
                        area = (float)p1.STArea();
                        points[0] = new PointF((float)p1.STCentroid().STX, (float)p1.STCentroid().STY);
                    }
                }
            }

            // return the point(s)
            return points;
        }

        //---------------------------------------------------------------------
        // get the list of points within a geometry
        //---------------------------------------------------------------------
        internal static String PointsfromRing(this SqlGeometry g, float height, float minx, float miny, float pointscale)
        {
            StringBuilder coords = new StringBuilder();

            // list each point in geometry
            for (int PointFIndex = 0; PointFIndex < g.STNumPoints(); PointFIndex++)
            {
                // get the point
                SqlGeometry geomPointF = g.STPointN(PointFIndex + 1);

                // translate to 0 coordinates and pointscale by scaling factor, then reduce to an int
                int px = Convert.ToInt32((((float)geomPointF.STX - minx) * pointscale));
                int py = Convert.ToInt32(height - (((float)geomPointF.STY - miny) * pointscale));

                // write results to output string
                coords.Append(px.ToString() + "," + py.ToString() + ",");
            }

            // remove trailing comma
            coords.Remove(coords.Length - 1, 1);

            return coords.ToString();
        }


    }

    //---------------------------------------------------------------------
    //---------------------------------------------------------------------
    // Classes. Subclass to extend standard classes with our own functionality
    //---------------------------------------------------------------------
    //---------------------------------------------------------------------
    
    /// <summary>
    /// Extra Functions for Colors
    /// </summary>
    public class ColorPlus
    {
        // create a random number generator 
        private static Random rand = new Random();

        // generate a color value in the range 0x00, 0x11, 0x22 ... 0xee, 0xff
        // for use with web browser & extended web colors
        private static int Next()
        {
            int color = 0;

            color = rand.Next(0, 16);
            color = color * 16 + color;

            return color;
        }

        /// <summary>
        /// returns a random Color
        /// </summary>
        public static Color Random
        {
            get
            {
                return Color.FromArgb(33, Next(), Next(), Next());
            }
        }
    }

}